<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        #cvs { /*是一个图片*/
            box-shadow: 5px 5px 15px 5px #333, -1px -1px 5px 3px #eee; /*阴影效果*/

            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%); /*让canvas居中于页面*/
        }

    </style>
</head>
<body>
<canvas id="cvs" width="450" height="450">


</canvas>
<script>
    //1、画棋盘：有15列，15行，每行30像素，每列30像素
    //获取画板
    var cvs = document.getElementById("cvs");
    //获取上下文
    var ctx = cvs.getContext("2d");

    var option={
        0:20,
        1:40,
        2:80,
        3:200,
        4:1000
    }
    init();
    var G = {//true的时候是黑子，false的时候是白子
        black: true
    }

    function drawPlain() {
        //循环画出行和列的直线
        ctx.strokeStyle = "#666";//改变线条颜色
        ctx.textBaseline="middle";//垂直对齐
        ctx.textAlign="center";//水平对齐
        for (var i = 0; i < 15; i++) {
            var x1 = 15, x2 = 435, y = 15 + (i * 30);
            //画行
            ctx.moveTo(x1, y);
            ctx.lineTo(x2, y);
            ctx.stroke();

            //画列
            ctx.moveTo(y, x1);
            ctx.lineTo(y, x2);
            ctx.stroke();

            ctx.beginPath();
            ctx.fillText(i, 3, y);
            ctx.closePath();

            ctx.beginPath();
            ctx.fillText(i, y, 9);
            ctx.closePath();//填充文字，参数：内容，x坐标，y坐标


        }
    }
    drawPlain();

    var lastPointBlack=null;


    function drawZi(x, y, black) {
        //把当前的子给一个样式
        if(lastPointBlack!=null){//把最后一粒子还原
            drawZi2(lastPointBlack.x,lastPointBlack.y,lastPointBlack.state==1)
        }
        lastPointBlack=getQiZi(x,y);
        x = x * 30 + 15;
        y = y * 30 + 15;
        ctx.beginPath();
        ctx.arc(x, y, 13, 0, 2 * Math.PI);
        ctx.closePath();
        /*var crg=ctx.createRadialGradient(100,150,20,100,150,0);
         crg.addColorStop(0,'red');
         crg.addColorStop(1,'green');*/
        if (black) {
            ctx.fillStyle = "red";
        } else {//描边
            ctx.fillStyle = "#eee";
            ctx.lineWidth = 2;
            ctx.strokeStyle = "#333";
            ctx.stroke();
        }
        ctx.fill();
    }
    //drawZi(false);//true为黑色，false为白色

    //2、画棋子：在行列线交叉处
    function drawZi2(x, y, black){
        //算出在x行y列的的像素坐标点
        x = x * 30 + 15;
        y = y * 30 + 15;
        ctx.beginPath();
        ctx.arc(x, y, 13, 0, 2 * Math.PI);
        ctx.closePath();
        /*var crg=ctx.createRadialGradient(100,150,20,100,150,0);
         crg.addColorStop(0,'red');
         crg.addColorStop(1,'green');*/
        if (black) {
            ctx.fillStyle = "#333";
        } else {
            ctx.fillStyle = "#eee";
            ctx.lineWidth = 2;
            ctx.strokeStyle = "#333";
            ctx.stroke();
        }
        ctx.fill();
    }
    function init() {//游戏的初始化
        //alert("111");
        var points = [];//二维数组，为了获取子方便
        var fangAns = [];
        var singlePoints=[];//一维数组，为了点的排序方便


        function Point(x, y) {//创建点的对象
            this.fangAns=[];
            this.x = x;
            this.y = y;
            this.state = 0; //0:没有子，1：黑子，2：白子
            this.score=0;//装评分的
        }

        function FangAn() {
            this.points = [];
            this.step = 0;//方案完成的程度
            this.state = 0;//0:没有子，1：全是黑子，2：全是白子 3、黑子和白子都有

            this.add = function (x, y) {//所有的方案添加到数组中
                var point = points[x][y];
                this.points.push(point);
                point.fangAns.push(this);//方案添加棋子的同时，棋子也添加方案
            }
        }

        //将所有的点存到二维数组中
        var point=null;//可以节省空间
        for (var i = 0; i < 15; i++) {
            this.fangAns=[];
            points[i] = [];
            for (var j = 0; j < 15; j++) {
                point=new Point(i, j);
                points[i][j] = point;
                singlePoints.push(point);
            }
        }


        //添加 列 方案
        var temp = null;

        for (var x = 0; x < 15; x++) {
            for (var y = 0; y < 11; y++) {
                temp = new FangAn();
                for (var k = 0; k < 5; k++) {
                    temp.add(x, y + k);
                }
                fangAns.push(temp);
            }
        }

        //添加横向方案
        for (var x = 0; x < 15; x++) {
            for (var y = 0; y < 11; y++) {
                temp = new FangAn();
                for (var k = 0; k < 5; k++) {
                    temp.add(y + k, x);
                }
                fangAns.push(temp);
            }
        }
        //添加斜（从左向右斜）的方案
        for (var x = 0; x < 11; x++) {
            for (var y = 0; y < 11; y++) {
                temp = new FangAn();
                for (var k = 0; k < 5; k++) {
                    temp.add(x + k, y + k);
                }
                fangAns.push(temp);
            }
        }
        //添加反斜的方案
        for (var x = 14; x > 3; x--) {
            for (var y = 0; y < 11; y++) {
                temp = new FangAn();
                for (var k = 0; k < 5; k++) {
                    temp.add(x - k, y + k);
                }
                fangAns.push(temp);
            }
        }

        window.points = points;//变成全局变量
        window.fangAns = fangAns;
        window.singlePoints=singlePoints;

    }


    //3、用户点击棋盘：画出相应的棋子，棋子要求黑白间隔（这一个是黑子，下一个要是白字）*/
    cvs.onclick = function (e) {
        //debugger;
        var x = e.offsetX;
        var y = e.offsetY;
        //算出在第几列第几行
        x = Math.floor(x / 30);
        y = Math.floor(y / 30);
        if (!hasZi(x, y)) {//没有子才画子
            //drawZi(x, y, (G.black = !G.black));
            handlerLuoZi(x,y,false);
        }
    }

    function getQiZi(x,y){
        if(x<0||x>14||y<0||y>14){
            console.error("这个(%d,%d)点不存在",x,y);//%d表示数字
            return;
        }
        return points[x][y];
    }

    function hasZi(x, y) {
        return points[x][y].state != 0;//返回假时，表示没有子
    }


    //4、判断输赢：满足一行、一列、斜线、反斜线任意一个5子相连都表示赢了，并且要显示赢家的颜色，棋盘不能再下棋
   function handlerLuoZi(x,y,black){
       //将这个子的己方方案的step累加，如果这个方案是对方的，那么这个方案作废
       //如果这个方案有效，并且已经连成了5个，也就是step==5，则棋局结束，black一方赢了
       var point=getQiZi(x,y);
       point.state = black ? 1 : 2; //计算机下完棋，这个G.black不会改变颜色
       drawZi(x, y, black)

       var arr=[2,1];
       if(point){
           for(var i in point.fangAns){//0:没有子，1：全是黑子，2：全是白子 3、黑子和白子都有
               var item=point.fangAns[i];
               var over=false;
               if(item.state==3){
                   continue;
               }
               if(item.state==0){//
                   item.state=arr[Boolean(black)*1];
               }
               if(item.state==arr[Boolean(black)*1]){//己方的方案
                    item.step++;
               }else if(item.state!=0){//对方的方案
                    item.state=3;
               }
               if(item.step==5){
                   over=true;
                   alert("棋局结束！"+(black?"黑方":"白方")+"赢了！");
                   cvs.onclick=function(){
                       alert("棋局结束！"+(black?"黑方":"白方")+"赢了！");
                   }
                   break;
               }
           }

       }
       if(!black&&!over){//如果白子下棋并且棋局没有结束，那么黑子--计算机下棋
           //alert("计算机下棋！");
           computerPlay();
       }
    }

    //5、实现一个算法：陪伴玩家下棋
    function computerPlay() {
        singlePoints = singlePoints.filter(function (item) {//已经有子的点去掉
            return item.state == 0;
        })
        for (var i in singlePoints) {//评分
            var point = singlePoints[i];
            point.score = 0;//评分不能累加
            point.score=point.fangAns.map(function(fangan){
                if(fangan.state==3){//作废的方案不评分
                    return 0;
                }
                return option[fangan.step];

            }).reduce(function(pre,cur){
                return pre+cur;
            });
        }
        //将评分进行排序
        singlePoints.sort(function(a,b){
            return b.score- a.score;
        });
        var greatPoint=singlePoints[0];
        handlerLuoZi(greatPoint.x,greatPoint.y,true);
    }
</script>
</body>
</html>